﻿namespace Journey.Caching
{
    using Enyim.Caching;
    using Enyim.Caching.Configuration;
    using Enyim.Caching.Memcached;

    using System;
    using System.Collections.Generic;
    using System.Net;

    /// <summary>
    /// Memcached 做缓存的缓存管理器。
    /// </summary>
    public class MemcachedCacheManager : ICacheManager
    {
        /// <summary>
        /// Memcached 客户端。
        /// </summary>
        private readonly MemcachedClient _memcachedClient;

        /// <summary>
        /// 初始化 Memcached 做缓存的缓存管理器。
        /// </summary>
        /// <param name="accountNumber"></param>
        /// <param name="password"></param>
        /// <param name="serverList"></param>
        public MemcachedCacheManager(string accountNumber, string password, IEnumerable<IPEndPoint> serverList)
        {
            var config = new MemcachedClientConfiguration();

            if (serverList != null)
            {
                foreach (var server in serverList)
                {
                    config.Servers.Add(new IPEndPoint(IPAddress.Parse(server.Address.ToString()), server.Port));
                }
            }

            config.Protocol = MemcachedProtocol.Text;
            config.Authentication.Type = typeof(PlainTextAuthenticator);
            config.Authentication.Parameters["userName"] = accountNumber;
            config.Authentication.Parameters["password"] = password;

            this._memcachedClient = new MemcachedClient(config);
        }

        /// <summary>
        /// 获取缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型。</typeparam>
        /// <param name="key">键对象。</param>
        /// <returns>值对象。</returns>
        public TValue Get<TValue>(string key) where TValue : class
        {
            var cache = (CacheEntity)this._memcachedClient.Get(key);

            return cache != null && cache.ExpiredTime > DateTime.UtcNow ? (TValue)cache.Value : default(TValue);
        }

        /// <summary>
        /// 获取缓存值，如果不存在，则添加缓存。
        /// </summary>
        /// <typeparam name="TValue">值类型。</typeparam>
        /// <param name="key">键对象。</param>
        /// <param name="acquire">获得值对象的委托。</param>
        /// <returns>值对象。</returns>
        public TValue GetOrAdd<TValue>(string key, Func<TValue> acquire) where TValue : class
        {
            var result = this.Get<TValue>(key) ?? acquire();

            this.Set(key, result);

            return result;
        }

        /// <summary>
        /// 增加缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型。</typeparam>
        /// <param name="key">键对象。</param>
        /// <param name="value">值对象。</param>
        public void Set<TValue>(string key, TValue value) where TValue : class
        {
            this.Set(key, value, DateTime.UtcNow.AddMinutes(20));
        }

        /// <summary>
        /// 增加缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型。</typeparam>
        /// <param name="key">键对象。</param>
        /// <param name="value">值对象。</param>
        /// <param name="expiredTime">过期时间(UTC)。</param>
        public void Set<TValue>(string key, TValue value, DateTime expiredTime) where TValue : class
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentNullException("key", "键对象不可以是空的。");

            var cache = new CacheEntity
            {
                Value = value,
                ExpiredTime = expiredTime
            };

            this._memcachedClient.Store(StoreMode.Set, key, cache);
        }

        /// <summary>
        /// 移除缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型。</typeparam>
        /// <param name="key">键对象。</param>
        /// <param name="result">值对象。</param>
        public void Remove<TValue>(string key, out TValue result) where TValue : class
        {
            result = this.Get<TValue>(key);

            if (result != null)
            {
                this._memcachedClient.Remove(key);
            }
        }

        /// <summary>
        /// 清空缓存。
        /// </summary>
        public void Clear()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 缓存实体。
        /// </summary>
        private class CacheEntity
        {
            /// <summary>
            /// 缓存值。
            /// </summary>
            public object Value { get; set; }

            /// <summary>
            /// 过期时间（UTC）
            /// </summary>
            public DateTime ExpiredTime { get; set; }
        }
    }
}